import org.junit.Test;
import static org.junit.Assert.*;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.HashMap;

public class BoardTests {

    /** Casa com o valor 1 significa preencher, casa com valor 0 significa nao preencher
     *
     */
    private Board criarCaminhoEmTabuleiro(int[][] casasAPreencher, int corAUsar) {

        Board boardADevolver = new Board(casasAPreencher.length);

        for (int y = 0; y != casasAPreencher.length; y++) {
            for (int x = 0; x != casasAPreencher[0].length; x++) {
                if (casasAPreencher[y][x] == 1) {
                    Move jogada = new Move(y, x);

                    boardADevolver.placePiece(corAUsar, jogada);
                }
            }
        }

        return boardADevolver;
    }

    @Test
    public void LinhaVerticalTabuleiroTamanho3PecasPretas() {
        /* Testar as seguintes situacoes:
         * 200  020  002
         * 200  020  002
         * 200  020  002
         */

        int[][] primeiroBoardMoves = {{1,0,0},
                                      {1,0,0},
                                      {1,0,0}};

        int[][] segundoBoardMoves = {{0,1,0},
                                     {0,1,0},
                                     {0,1,0}};

        int[][] terceiroBoardMoves = {{0,0,1},
                                      {0,0,1},
                                      {0,0,1}};

        Board primeiroTabuleiro = criarCaminhoEmTabuleiro(primeiroBoardMoves, Color.BLACK);
        Board segundoTabuleiro = criarCaminhoEmTabuleiro(segundoBoardMoves, Color.BLACK);
        Board terceiroTabuleiro = criarCaminhoEmTabuleiro(terceiroBoardMoves, Color.BLACK);

        assertTrue(primeiroTabuleiro.hasBlackWon());
        assertTrue(segundoTabuleiro.hasBlackWon());
        assertTrue(terceiroTabuleiro.hasBlackWon());

        // Experimentar com um tabuleiro vazio
        Board quartoTabuleiro = new Board(3);

        assertFalse(quartoTabuleiro.hasBlackWon());
    }

    @Test
    public void LinhaDiagonal1TabuleiroTamanho3PecasPretas() {
        /* Testar as seguintes situacoes:
         * 200  200  220  220  222
         * 220  222  020  022  002
         * 020  002  020  002  002
         */

        int[][] primeiroBoardMoves = {{1,0,0},
                                      {1,1,0},
                                      {0,1,0}};

        int[][] segundoBoardMoves = {{1,0,0},
                                     {1,1,1},
                                     {0,0,1}};

        int[][] terceiroBoardMoves = {{1,1,0},
                                      {0,1,0},
                                      {0,1,0}};

        int[][] quartoBoardMoves = {{1,1,0},
                                    {0,1,1},
                                    {0,0,1}};

        int[][] quintoBoardMoves = {{1,1,1},
                                    {0,0,1},
                                    {0,0,1}};

        Board primeiroTabuleiro = criarCaminhoEmTabuleiro(primeiroBoardMoves, Color.BLACK);
        Board segundoTabuleiro = criarCaminhoEmTabuleiro(segundoBoardMoves, Color.BLACK);
        Board terceiroTabuleiro = criarCaminhoEmTabuleiro(terceiroBoardMoves, Color.BLACK);
        Board quartoTabuleiro = criarCaminhoEmTabuleiro(quartoBoardMoves, Color.BLACK);
        Board quintoTabuleiro = criarCaminhoEmTabuleiro(quintoBoardMoves, Color.BLACK);

        assertTrue(primeiroTabuleiro.hasBlackWon());
        assertTrue(segundoTabuleiro.hasBlackWon());
        assertTrue(terceiroTabuleiro.hasBlackWon());
        assertTrue(quartoTabuleiro.hasBlackWon());
        assertTrue(quintoTabuleiro.hasBlackWon());
    }

    @Test
    public void LinhaDiagonal2TabuleiroTamanho3PecasPretas() {
        /* Testar as seguintes situacoes:
         * 020  020  020  022  020  002  002  022  222
         * 200  020  022  002  220  020  002  200  200
         * 200  200  002  002  200  200  020  200  200
         */

        int[][] primeiroBoardMoves = {{0,1,0},
                                      {1,0,0},
                                      {1,0,0}};

        int[][] segundoBoardMoves = {{0,1,0},
                                     {0,1,0},
                                     {1,0,0}};

        int[][] terceiroBoardMoves = {{0,1,0},
                                      {0,1,1},
                                      {0,0,1}};

        int[][] quartoBoardMoves = {{0,1,1},
                                    {0,0,1},
                                    {0,0,1}};

        int[][] quintoBoardMoves = {{0,1,0},
                                    {1,1,0},
                                    {1,0,0}};

        int[][] sextoBoardMoves = {{0,0,1},
                                   {0,1,0},
                                   {1,0,0}};

        int[][] setimoBoardMoves = {{0,0,1},
                                    {0,0,1},
                                    {0,1,0}};

        int[][] oitavoBoardMoves = {{0,1,1},
                                    {1,0,0},
                                    {1,0,0}};

        int[][] nonoBoardMoves = {{1,1,1},
                                  {1,0,0},
                                  {1,0,0}};

        Board primeiroTabuleiro = criarCaminhoEmTabuleiro(primeiroBoardMoves, Color.BLACK);
        Board segundoTabuleiro = criarCaminhoEmTabuleiro(segundoBoardMoves, Color.BLACK);
        Board terceiroTabuleiro = criarCaminhoEmTabuleiro(terceiroBoardMoves, Color.BLACK);
        Board quartoTabuleiro = criarCaminhoEmTabuleiro(quartoBoardMoves, Color.BLACK);
        Board quintoTabuleiro = criarCaminhoEmTabuleiro(quintoBoardMoves, Color.BLACK);
        Board sextoTabuleiro = criarCaminhoEmTabuleiro(sextoBoardMoves, Color.BLACK);
        Board setimoTabuleiro = criarCaminhoEmTabuleiro(setimoBoardMoves, Color.BLACK);
        Board oitavoTabuleiro = criarCaminhoEmTabuleiro(oitavoBoardMoves, Color.BLACK);
        Board nonoTabuleiro = criarCaminhoEmTabuleiro(nonoBoardMoves, Color.BLACK);

        assertTrue(primeiroTabuleiro.hasBlackWon());
        assertTrue(segundoTabuleiro.hasBlackWon());
        assertTrue(terceiroTabuleiro.hasBlackWon());
        assertTrue(quartoTabuleiro.hasBlackWon());
        assertTrue(quintoTabuleiro.hasBlackWon());
        assertTrue(sextoTabuleiro.hasBlackWon());
        assertTrue(setimoTabuleiro.hasBlackWon());
        assertTrue(oitavoTabuleiro.hasBlackWon());
        assertTrue(nonoTabuleiro.hasBlackWon());
    }

	@Test
    public void LinhaVerticalTabuleiroTamanho3PecasBrancas() {
        /* Testar as seguintes situacoes:
         * 111  000  000
         * 000  111  111
         * 000  000  000
         */

        int[][] primeiroBoardMoves = {{1,1,1},
                                      {0,0,0},
                                      {0,0,0}};

        int[][] segundoBoardMoves = {{0,0,0},
                                     {1,1,1},
                                     {0,0,0}};

        int[][] terceiroBoardMoves = {{0,0,0},
                                      {0,0,0},
                                      {1,1,1}};

        Board primeiroTabuleiro = criarCaminhoEmTabuleiro(primeiroBoardMoves, Color.WHITE);
        Board segundoTabuleiro = criarCaminhoEmTabuleiro(segundoBoardMoves, Color.WHITE);
        Board terceiroTabuleiro = criarCaminhoEmTabuleiro(terceiroBoardMoves, Color.WHITE);

        assertTrue(primeiroTabuleiro.hasWhiteWon());
        assertTrue(segundoTabuleiro.hasWhiteWon());
        assertTrue(terceiroTabuleiro.hasWhiteWon());

        // Experimentar com um tabuleiro vazio
        Board quartoTabuleiro = new Board(3);

        assertFalse(quartoTabuleiro.hasWhiteWon());
    }

    @Test
    public void LinhaDiagonal1TabuleiroTamanho3PecasBrancas() {
        /* Testar as seguintes situacoes:
         * 110  110  100  100  100
         * 011  010  111  110  100
         * 000  011  000  011  111
         */						  
								  
        int[][] primeiroBoardMoves = {{1,1,0},
                                      {0,1,1},
                                      {0,0,0}};
								  
        int[][] segundoBoardMoves = {{1,1,0},
                                     {0,1,0},
                                     {0,1,1}};
								  
        int[][] terceiroBoardMoves = {{1,0,0},
                                      {1,1,1},
                                      {0,0,0}};
								  
        int[][] quartoBoardMoves = {{1,0,0},
                                    {1,1,0},
                                    {0,1,1}};
								  
        int[][] quintoBoardMoves = {{1,0,0},
                                    {1,0,0},
                                    {1,1,1}};
								  
        Board primeiroTabuleiro = criarCaminhoEmTabuleiro(primeiroBoardMoves, Color.WHITE);
        Board segundoTabuleiro = criarCaminhoEmTabuleiro(segundoBoardMoves, Color.WHITE);
        Board terceiroTabuleiro = criarCaminhoEmTabuleiro(terceiroBoardMoves, Color.WHITE);
        Board quartoTabuleiro = criarCaminhoEmTabuleiro(quartoBoardMoves, Color.WHITE);
        Board quintoTabuleiro = criarCaminhoEmTabuleiro(quintoBoardMoves, Color.WHITE);
								  
        assertTrue(primeiroTabuleiro.hasWhiteWon());
        assertTrue(segundoTabuleiro.hasWhiteWon());
        assertTrue(terceiroTabuleiro.hasWhiteWon());
        assertTrue(quartoTabuleiro.hasWhiteWon());
        assertTrue(quintoTabuleiro.hasWhiteWon());
    }							  
								  
    @Test						  
    public void LinhaDiagonal2TabuleiroTamanho3PecasBrancas() {
        /* Testar as seguintes situacoes:	  
         * 011  001  000  000  011  001  000  011  111
         * 100 	110  110  100  110  010  001  100  100
         * 000  000  011  111  000  100  110  100  100
         */						  
		   						  
        int[][] primeiroBoardMoves = {{0,1,1},
                                      {1,0,0},
                                      {0,0,0}};
		   						  
        int[][] segundoBoardMoves = {{0,0,1},
                                     {1,1,0},
                                     {0,0,0}};
		   						  
        int[][] terceiroBoardMoves = {{0,0,0},
		   						  	  {1,1,0},
		   						  	  {0,1,1}};
		   						  
        int[][] quartoBoardMoves = {{0,0,0},
                                    {1,0,0},
                                    {1,1,1}};
		   						  
        int[][] quintoBoardMoves = {{0,1,1},
                                    {1,1,0},
                                    {0,0,0}};

        int[][] sextoBoardMoves = {{0,0,1},
                                   {0,1,0},
                                   {1,0,0}};

        int[][] setimoBoardMoves = {{0,0,0},
                                    {0,0,1},
                                    {1,1,0}};

        int[][] oitavoBoardMoves = {{0,1,1},
                                    {1,0,0},
                                    {1,0,0}};

        int[][] nonoBoardMoves = {{1,1,1}, 
                                  {1,0,0}, 
                                  {1,0,0}};

        Board primeiroTabuleiro = criarCaminhoEmTabuleiro(primeiroBoardMoves, Color.WHITE);
        Board segundoTabuleiro = criarCaminhoEmTabuleiro(segundoBoardMoves, Color.WHITE);
        Board terceiroTabuleiro = criarCaminhoEmTabuleiro(terceiroBoardMoves, Color.WHITE);
        Board quartoTabuleiro = criarCaminhoEmTabuleiro(quartoBoardMoves, Color.WHITE);
        Board quintoTabuleiro = criarCaminhoEmTabuleiro(quintoBoardMoves, Color.WHITE);
        Board sextoTabuleiro = criarCaminhoEmTabuleiro(sextoBoardMoves, Color.WHITE);
        Board setimoTabuleiro = criarCaminhoEmTabuleiro(setimoBoardMoves, Color.WHITE);
        Board oitavoTabuleiro = criarCaminhoEmTabuleiro(oitavoBoardMoves, Color.WHITE);
        Board nonoTabuleiro = criarCaminhoEmTabuleiro(nonoBoardMoves, Color.WHITE);

        assertTrue(primeiroTabuleiro.hasWhiteWon());
        assertTrue(segundoTabuleiro.hasWhiteWon());
        assertTrue(terceiroTabuleiro.hasWhiteWon());
        assertTrue(quartoTabuleiro.hasWhiteWon());
        assertTrue(quintoTabuleiro.hasWhiteWon());
        assertTrue(sextoTabuleiro.hasWhiteWon());
        assertTrue(setimoTabuleiro.hasWhiteWon());
        assertTrue(oitavoTabuleiro.hasWhiteWon());
        assertTrue(nonoTabuleiro.hasWhiteWon());
    }

	private Map<Move, Integer> movesNecessariosParaRecriarTabuleiro(int[][] board) {
		// Moves necessarios para criar este board
		// A chave do tipo Integer corresponde a cor da peca que esta
		// a fazer o move
		Map<Move, Integer> movesNecessarios = new HashMap<Move, Integer>();
		
		for (int y = 0; y != board.length; y++) {
			for (int x = 0; x != board.length; x++) {
				if (board[y][x] == Color.WHITE) {
					movesNecessarios.put(new Move(y, x), Color.WHITE);
				}
				else if (board[y][x] == Color.BLACK) {
					movesNecessarios.put(new Move(y, x), Color.BLACK);
				}
			}
		}

		return movesNecessarios;
	}

	/** Para testar o algoritmo para detectar caminhos, para além dos
	 * testes que foram feitos nos outros testes que estao neste
	 * ficheiro, tambem foram corridos jogos CPU vs. CPU na interface
	 * em si.
	 *
	 * Estes testes indicam situacoes em que o programa indicou que
	 * havia um caminho entre as paredes, quando na realidade nao
	 * existia.
	 *
	 * No entanto, este problema parece acontecer de forma aleatoria,
	 * por exemplo, se tentar replicar o tabuleiro usando o sistema de
	 * Humano vs. Humano, o problema ja nao aparece.
	 *
	 * Estes testes unitarios servem para confirmar que o problema nao
	 * vem daqui.
	 */
	@Test
	public void erro1OcurridoDuranteTeste() {
		/*   A B C D E F G H I 
		 * A  _ X _ O _ X O _ X 
		 *  B  X O _ _ _ _ _ X X 
		 *   C  X _ _ _ _ O _ X X 
		 *    D  _ _ _ _ _ _ _ _ _ 
		 *     E  _ _ O O _ _ _ _ O 
		 *      F  _ _ _ _ O X O O _ 
		 *       G  O _ _ X _ _ X _ _ 
		 *        H  _ O O _ _ _ _ _ _ 
		 *         I  _ _ _ _ _ _ _ _ _ 
		 * 
		 * [0, 2, 0, 1, 0, 2, 1, 0, 2]
		 * [2, 1, 0, 0, 0, 0, 0, 2, 2]
		 * [2, 0, 0, 0, 0, 1, 0, 2, 2]
		 * [0, 0, 0, 0, 0, 0, 0, 0, 0]
		 * [0, 0, 1, 1, 0, 0, 0, 0, 1]
		 * [0, 0, 0, 0, 1, 2, 1, 1, 0]
		 * [1, 0, 0, 2, 0, 0, 2, 0, 0]
		 * [0, 1, 1, 0, 0, 0, 0, 0, 0]
		 * [0, 0, 0, 0, 0, 0, 0, 0, 0]
		 */
		// Victoria para as pecas brancas

		int[][] board = {{0, 2, 0, 1, 0, 2, 1, 0, 2},
						 {2, 1, 0, 0, 0, 0, 0, 2, 2},
						 {2, 0, 0, 0, 0, 1, 0, 2, 2},
						 {0, 0, 0, 0, 0, 0, 0, 0, 0},
						 {0, 0, 1, 1, 0, 0, 0, 0, 1},
						 {0, 0, 0, 0, 1, 2, 1, 1, 0},
						 {1, 0, 0, 2, 0, 0, 2, 0, 0},
						 {0, 1, 1, 0, 0, 0, 0, 0, 0},
						 {0, 0, 0, 0, 0, 0, 0, 0, 0}};

		Board tabuleiro = new Board(board.length);

		for (Map.Entry<Move, Integer> jogada : movesNecessariosParaRecriarTabuleiro(board).entrySet()) {
			tabuleiro.placePiece(jogada.getValue(), jogada.getKey());
		}

		assertFalse(tabuleiro.hasWhiteWon());
	}

	@Test
	public void erro2OcurridoDuranteTeste() {
		/*   A B C D E F 
		 * A  _ _ _ _ X _ 
		 *  B  _ _ _ O _ O 
		 *   C  O _ _ X _ _ 
		 *    D  _ _ X _ _ _ 
		 *     E  _ _ _ _ _ O 
		 *      F  X _ O X O X 
		 * 
		 * [0, 0, 0, 0, 2, 0]
		 * [0, 0, 0, 1, 0, 1]
		 * [1, 0, 0, 2, 0, 0]
		 * [0, 0, 2, 0, 0, 0]
		 * [0, 0, 0, 0, 0, 1]
		 * [2, 0, 1, 2, 1, 2]
		 */
		// Vitoria para as pecas pretas
		int[][] board =  {{0, 0, 0, 0, 2, 0},
						  {0, 0, 0, 1, 0, 1},
						  {1, 0, 0, 2, 0, 0},
						  {0, 0, 2, 0, 0, 0},
						  {0, 0, 0, 0, 0, 1},
						  {2, 0, 1, 2, 1, 2}};

		Board tabuleiro = new Board(board.length);

		for (Map.Entry<Move, Integer> jogada : movesNecessariosParaRecriarTabuleiro(board).entrySet()) {
			tabuleiro.placePiece(jogada.getValue(), jogada.getKey());
		}

		assertFalse(tabuleiro.hasWhiteWon());
	}
}
